LIBRARY IEEE; 
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

--Maquina de Estados de Controle de Sinais

ENTITY control_sm IS

    PORT(clock, start_test: IN STD_LOGIC := '0';
        ic_found: IN STD_LOGIC_VECTOR(1 DOWNTO 0) := "10";
        ic: IN STD_LOGIC_VECTOR(11 DOWNTO 0) := "000000000000";
        reset: OUT STD_LOGIC := '1';
        filter_enable: OUT STD_LOGIC := '0';
        address: OUT STD_LOGIC_VECTOR(9 DOWNTO 0) := "0000000000";
        LCD_address: OUT STD_LOGIC_VECTOR(9 DOWNTO 0) := "0000000000";
        test_state: OUT STD_LOGIC_VECTOR(4 DOWNTO 0) := "00000";
        result: OUT STD_LOGIC := '0');

END control_sm;

ARCHITECTURE logic OF control_sm IS

TYPE states IS (START, RESET_ALL, WAITING_START, FIRST_CHECK,
    ANALYSIS_0, ANALYSIS_1, ANALYSIS_2, ANALYSIS_3, ANALYSIS_4,
    TESTING, ADD_ADDR, UPDATE_RESULT, SEE_RESULT, CLEAN_REGS,
    NEW_TEST, TEST_1, TEST_2, TEST_3, TEST_X, END_TEST,
    FINAL_CHECK, IC_1_CHECK, IC_2_CHECK, IC_3_CHECK, IC_4_CHECK, IC_5_CHECK,
    IC_6_CHECK, IC_7_CHECK, IC_8_CHECK, IC_9_CHECK, UNKNOWN_IC,
	 LCD_CHECK, WRITING_LCD, ADD_LCD_ADDR);

SIGNAL state: states := START;
SIGNAL next_state: states := START;

SIGNAL addr, addr2: STD_LOGIC_VECTOR(9 DOWNTO 0) := "0000000000";
SIGNAL s_state: STD_LOGIC_VECTOR(4 DOWNTO 0) := "00000";

BEGIN

    address <= addr;
	 LCD_address <= addr2;
    test_state <= s_state;

    PROCESS(clock)
    VARIABLE ciclos: INTEGER RANGE 0 TO 100;
    BEGIN
        IF(clock = '1' AND clock'EVENT) THEN
            CASE state IS
                WHEN START =>
                    reset <= '1';
                    state <= RESET_ALL;
                    next_state <= RESET_ALL;
                WHEN RESET_ALL =>
                    filter_enable <= '0';
                    addr <= "0000000000";
                    s_state <= "00000";
                    ciclos := 0;
                    state <= WAITING_START;
                WHEN WAITING_START =>
                    IF(start_test = '1')THEN
                        reset <= '0';
                        state <= FIRST_CHECK;
                    ELSE
                        state <= WAITING_START;
                    END IF;
                WHEN FIRST_CHECK =>
                    filter_enable <= '1';
                    state <= ANALYSIS_0;
                WHEN ANALYSIS_0 =>
                    filter_enable <= '0';
                    IF(ic_found = "00") THEN
                        state <= SEE_RESULT;
                    ELSIF(ic_found = "01") THEN -- WILL DETECT 7400 HERE
                        state <= FINAL_CHECK;
                    ELSIF(ic_found = "11") THEN
                        state <= TEST_X; --IMPOSSIBLE CASE
                    ELSE
                        IF(ic(1) = '1')THEN
                            state <= TEST_1; -- CONFLICT AMONG AND, OR, XOR, BCD, MUX2, COUNTER, REGISTERS
                        ELSIF(ic(4) = '1')THEN
                            state <= FINAL_CHECK; --WILL DETECT 7404 HERE
                        ELSIF(ic(5) = '1')THEN
                            state <= FINAL_CHECK; --WILL DETECT 7410 HERE
                        ELSIF(ic(6) = '1')THEN
                            state <= FINAL_CHECK; --WILL DETECT 7420 HERE
                        ELSIF(ic(7) = '1')THEN
                            state <= FINAL_CHECK; --WILL DETECT 74107 HERE
                        ELSE
                            state <= UNKNOWN_IC; --WRONG OR UNKNOWN CI;
                        END IF;
                    END IF;
                WHEN ANALYSIS_1 =>
                    filter_enable <= '0';
                    IF(ic_found = "00") THEN
                        state <= SEE_RESULT;
                    ELSIF(ic_found = "01") THEN
                        state <= FINAL_CHECK;
                    ELSE
                        IF(ic(1) = '1' OR ic(2) = '1' OR ic(3) = '1')THEN
                            state <= FINAL_CHECK;
                        ELSE
                            state <= TEST_2;
                        END IF;
                    END IF;
                WHEN ANALYSIS_2 =>
                    filter_enable <= '0';
                    IF(ic_found = "00") THEN
                        state <= SEE_RESULT;
                    ELSIF(ic_found = "01") THEN
                        state <= FINAL_CHECK;
                    ELSE
                        IF(ic(8) = '1' OR ic(9) = '1')THEN -- WILL DETECT 7448 OR 74153 HERE
                            state <= FINAL_CHECK;
                        ELSE
                            state <= TEST_3;
                        END IF;
                    END IF;
                WHEN ANALYSIS_3 =>
                    filter_enable <= '0';
                    IF(ic_found = "00") THEN
                        state <= SEE_RESULT;
                    ELSIF(ic_found = "01") THEN -- WILL DETECT 74163 HERE
                        state <= FINAL_CHECK;
                    ELSE
                        state <= FINAL_CHECK; -- WILL DETECT 74173 HERE
                    END IF;
                WHEN TESTING =>			  
                    IF(ciclos = 0 OR ic_found = "00") THEN
                        filter_enable <= '0';
                        s_state <= "00000";
                        state <= UPDATE_RESULT; 
                    ELSE
                        state <= ADD_ADDR;
                    END IF;
                WHEN UPDATE_RESULT =>
                    state <= next_state;
                WHEN SEE_RESULT =>
                    reset <= '1';
                    state <= LCD_CHECK;
                WHEN NEW_TEST =>
                    result <= '0';
                    state <= START;
                WHEN ADD_ADDR =>
                    addr <= addr + '1';
                    ciclos := ciclos - 1;
                    state <= TESTING;
                WHEN TEST_1 => 
                    filter_enable <= '1';
                    next_state <= ANALYSIS_1; 
                    addr <= "0000000011";
                    s_state <= "00001";
                    ciclos := 2;	
                    state <= TESTING;
                WHEN TEST_2 => 
                    filter_enable <= '1';
                    next_state <= ANALYSIS_2; 
                    addr <= "0000001000";
                    s_state <= "00010";
                    ciclos := 3;	
                    state <= TESTING;
                WHEN TEST_3 => 
                    filter_enable <= '1';
                    next_state <= ANALYSIS_3; 
                    addr <= "0010110110";
                    s_state <= "00011";
                    ciclos := 4;	
                    state <= TESTING;
                WHEN TEST_X =>
                    filter_enable <= '1';
                    next_state <= END_TEST; 
                    addr <= "1111111111";
                    s_state <= "11111";
                    ciclos := 0;	
                    state <= TESTING;
                WHEN FINAL_CHECK =>
                    IF(ic(0) = '1' OR ic(1) = '1' OR ic(2) = '1' OR ic(3) = '1') THEN
                        state <= IC_1_CHECK;
                    ELSIF(ic(4) = '1') THEN
                        state <= IC_2_CHECK;
                    ELSIF(ic(5) = '1') THEN
                        state <= IC_3_CHECK;	
                    ELSIF(ic(6) = '1') THEN
                        state <= IC_4_CHECK;
                    ELSIF(ic(7) = '1') THEN
                        state <= IC_5_CHECK;
                    ELSIF(ic(8) = '1') THEN
                        state <= IC_6_CHECK;
                    ELSIF(ic(9) = '1') THEN
                        state <= IC_7_CHECK;	
                    ELSIF(ic(10) = '1') THEN
                        state <= IC_8_CHECK;
                    ELSIF(ic(11) = '1') THEN
                        state <= IC_9_CHECK;
                    ELSE
                        state <= SEE_RESULT; 
                    END IF;
                WHEN IC_1_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0000000001";
                    s_state <= "10001";
                    ciclos := 4; 
                    state <= TESTING;
                WHEN IC_2_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0000000110";
                    s_state <= "10010";
                    ciclos := 2; 
                    state <= TESTING;
                WHEN IC_3_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0000001101";
                    s_state <= "10011";
                    ciclos := 8; 
                    state <= TESTING;
                WHEN IC_4_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0000010111";
                    s_state <= "10100";
                    ciclos := 16; 
                    state <= TESTING;
                WHEN IC_5_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0000101001";
                    s_state <= "10101";
                    ciclos := 13; 
                    state <= TESTING;
                WHEN IC_6_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0000111000";
                    s_state <= "10110";
                    ciclos := 19; 
                    state <= TESTING;
                WHEN IC_7_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0001001100";
                    s_state <= "10111";
                    ciclos := 11; 
                    state <= TESTING;
                WHEN IC_8_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0001011010";
                    s_state <= "11000";
                    ciclos := 67; 
                    state <= TESTING;
                WHEN IC_9_CHECK =>
                    filter_enable <= '1';
                    next_state <= SEE_RESULT;
                    addr <= "0010011111";
                    s_state <= "11001";
                    ciclos := 20; 
                    state <= TESTING;
                WHEN LCD_CHECK =>
                    state <= WRITING_LCD;
                    IF(ic(0) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(1) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(2) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(3) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;	
                    ELSIF(ic(4) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(5) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(6) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26; 
                    ELSIF(ic(7) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(8) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(9) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(10) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSIF(ic(11) = '1') THEN
                        addr2 <= "0100000000";
                        ciclos := 26;
                    ELSE
                        addr2 <= "0100011011";
                        ciclos := 38;
                    END IF;
                WHEN WRITING_LCD =>			  
                    IF(ciclos = 0) THEN
                        state <= NEW_TEST; 
                    ELSE
                        state <= ADD_LCD_ADDR;
                    END IF;
                WHEN ADD_LCD_ADDR =>
                    addr2 <= addr2 + '1';
                    ciclos := ciclos - 1;
                    state <= WRITING_LCD;
                WHEN OTHERS =>
                    filter_enable <= '0';
                    state <= END_TEST;
            END CASE;
        END IF;
    END PROCESS;

END logic;